/*
* Author: Dzlua
* QQ    : 505544956
* time  : 2017/08/11
*/
#pragma once

#include <thread>
#include <future>
#include <memory>
#include <chrono>
#include <functional>

namespace dzlua {

class CallAfter {
public:
    /*
    *   after 'after' T time, it will call with sync.
    *   return a future.
    */
    template<class _Rep, class _Period, class Fun, class... Args>
    static auto SyncCall(const std::chrono::duration<_Rep, _Period> &after, Fun&& fun, Args&&... args) 
            -> std::future<typename std::result_of<Fun(Args...)>::type> {
        
        /*
        *   make a task.
        */
        using return_type = typename std::result_of<Fun(Args...)>::type;
        auto task = std::make_shared< std::packaged_task<return_type()> >(
                std::bind(std::forward<Fun>(fun), std::forward<Args>(args)...)
            );

        /*
        *   make a future.
        */    
        std::future<return_type> res = task->get_future();
        
        // wait
        std::this_thread::sleep_for(after);
        
        // after call.
        (*task)(); 

        return res;
    }
    
    /*
    *   after 'after' T time, it will call with async.
    *   return a future.
    */
    template<class _Rep, class _Period, class Fun, class... Args>
    static auto AsyncCall(const std::chrono::duration<_Rep, _Period> &after, Fun&& fun, Args&&... args) 
            -> std::future<typename std::result_of<Fun(Args...)>::type> {
        
        /*
        *   make a task.
        */
        using return_type = typename std::result_of<Fun(Args...)>::type;
        auto task = std::make_shared< std::packaged_task<return_type()> >(
                std::bind(std::forward<Fun>(fun), std::forward<Args>(args)...)
            );

        /*
        *   make a future.
        */    
        std::future<return_type> res = task->get_future();
        
        // create a thread and call
        std::thread([task, after](){
            // wait
            std::this_thread::sleep_for(after);

            // after call.
            (*task)();

        }).detach();

        return res;
    }
}; // end class CallAfter 

} // end namespace dzlua
